package lino;
import lino.node.*;
import lino.analysis.*;
import lino.parser.*;
import lino.lexer.*;
import java.io.*;
import java.util.*;
class sccCompiler extends Lc {
    PAbsexpr[][] tiling;
    int[][] hostnums = { {0, 2, 4, 6, 8, 10}
, { 1, 3, 5, 7, 9, 11}
, { 12, 14, 16, 18, 20, 22}
, { 13, 15, 17, 19, 21, 23}
, { 24, 26, 28, 30, 32, 34}
, { 25, 27, 29, 31, 33, 35}
, { 36, 38, 40, 42, 44, 46}
, { 37, 39, 41, 43, 45, 47}
    } ;
    boolean onEdge(int dir, int row, int col) {
    // this returns true if the edge of the cell at position row col, going in direction dir
    // is on the very edge of the occupied array of cells
      int newrow = row + dy[dir];
      int newcol = col + dx[dir];
      if (newrow < 0) {
        return true;
      }

      if (newcol < 0) {
        return true;
      }

      if (newrow >= tiling.length) {
        return true;
      }

      if (newcol >= tiling[0].length) {
        return true;
      }

      return false;
    }
    String comsprog = "/shared/paulcockshott/rcce/apps/HELLO/hello 2 0.533 ";
    String setUpTransmission(String filename, int sourcecore, int destcore)  /** Produces a string which when executed as a shell command on source core will copy the data in
   *  the file called filename to the core destcore. Must run in conjunction with the command to set up reception.
   */ {
      return comsprog + String.format("%02d ", sourcecore) + String.format("%02d ", destcore) + "--from " + filename + " --to /dev/null &";
    }
    String setUpReception(String filename, int sourcecore, int destcore)  /** Produces a string which when executed as a shell command on source core will copy the data in
   *  the file called filename to the core destcore.
   */ {
      return comsprog + String.format("%02d ", sourcecore) + String.format("%02d ", destcore) + "--from /dev/null --to " + filename + "  &";
    }
    sccCompiler(PAbsexpr[][] t){ tiling = t; }
    void compile(PrintStream printer) {
      int y = tiling.length; int x = tiling[0].length;
      if (y > 8 || x > 6) { System.out.println("error: task array to large to fit on chip"); return; }
    // for each position in the tiling we
    // generate 4 output fifo names
      String[][][] outputFifoNames = new String[y][x][4];
      String[][][] defaultoutputFifoNames = new String[y][x][4];
    // the input fifo names will be generated after
    // taking into account the presence
    // of mirror null and identity cells
      String[][][] inputFifoNames = new String[y][x][4];


    // first we generate a default pattern of output fifo names
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            for (int dir = North; dir <= West; dir++) {
              String fifoname = fifodir + fifoprefix[dir] + row + "_" + col;

              defaultoutputFifoNames[row][col][dir] = outputFifoNames[row][col][dir] = fifoname;

            }
        }
      }

    // now apply rotations and flips to the fifo output  positions
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            resolveAtomicRotateAndFLip(tiling[row][col], outputFifoNames[row][col], 1);
      // resolveAtomicRotateAndFLip(tiling[row][col], inputFifoNames[row][col],1);
        }

      }
      printer.println("case `hostname` in");
    // now route the input channels
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            printer.println("rck" + String.format("%02d", hostnums[row][col]) + ")");
            for (int dir = North; dir <= West; dir++) {
              if (! onEdge(dir, row, col)) {
                String fifoname = fifodir + fifoprefix[dir] + row + "_" + col;
                printer.println("mkfifo " + fifoname);

                printer.println("mkfifo " + (inputFifoNames[row][col][dir] = fifoname + "in"));
                printer.println(setUpTransmission(fifoname, hostnums[row][col], hostnums[row + dy[dir]][col + dx[dir]]));
                printer.println(setUpReception(fifoname + "in", hostnums[row + dy[dir]][col + dx[dir]], hostnums[row][col]));

              }

            }
            if (! routingcell(tiling[row][col])) {
        //  for (int dir = 0; dir < 4; dir++) {
        //    inputFifoNames[row][col][dir] = trace(dir, row, col, outputFifoNames, tiling, tiling[row][col] instanceof AMirrorAbsexpr);
        //  }

              tiling[row][col] = resolveAtomicRotateAndFLip(tiling[row][col], inputFifoNames[row][col], 1);
              if (tiling[row][col] instanceof ABlockAbsexpr) {
                LinkedList<PAbsioassign> list = ((ABlockAbsexpr) tiling[row][col]) .getAbsioassign();
                Object[] assigns = list.toArray();
                printer.println("# starting block ");
                for (int i = 0; i < assigns.length; i++) {
          // handle io redirection for blocks
                    String command = "";
                    AAbsioassign assign = (AAbsioassign) assigns[i];

                    command = (assign).getIdsym().toString();

                    Process thisTask = new Process(command);
                    printer.println("# command " + i + " = " + command);
                    for (PAbsparams p : assign.getAbsparams()) {
                      if (p instanceof ARedirectionAbsparams) {
                        ARedirectionAbsparams rd = (ARedirectionAbsparams) p;
                        String op = rd.getInoutsym().getText();
                        String[] fn = op.equals("<") ? inputFifoNames[row][col] : defaultoutputFifoNames[row][col];
                        thisTask.add(new Rdir( filenamefor(fn, rd.getAbsstream().toString().trim()), rd.getInoutsym().getText()));
                      } else {
                        AStreamAbsparams s = (AStreamAbsparams) p;
                        thisTask.add(new Rdir( filenamefor(inputFifoNames[row][col], s.toString()), ""));
                      }
                    }
                    ;

                    printer.println(thisTask.toString() + " &");
                }
              } else {
                if (tiling[row][col] instanceof AMirrorAbsexpr) {
                    for (int dir = 0; dir < 4; dir++) {
                      Process thisTask = new Process("cat ");
                      thisTask.add(new Rdir(inputFifoNames[row][col][dir], " <"));
                      thisTask.add(new Rdir(outputFifoNames[row][col][dir], " >"));
                      if (! (inputFifoNames[row][col][dir].equals("/dev/null"))) {
                        printer.println(thisTask.toString() + " &");
                      }

                    }
                } else {
                    String command = getShellPrimitive(tiling[row][col]) + " ";
                    Process thisTask = new Process(command);
                    for (int dir = 0; dir < 4; dir++) {
                      thisTask.add(new Rdir(inputFifoNames[row][col][dir], " " + shellinputs[dir] + "<"));
                      thisTask.add(new Rdir(outputFifoNames[row][col][dir], " " + shelloutputs[dir] + ">"));
                    }
                    printer.println(thisTask.toString() + " " + row + " " + col + "&");
                }
              }

            }
            printer.println("wait");
            for (int dir = North; dir <= West; dir++) {
              if (! onEdge(dir, row, col)) {
                String fifoname = fifodir + fifoprefix[dir] + row + "_" + col;
                printer.println("rm " + fifoname);

                printer.println("rm " + (inputFifoNames[row][col][dir] = fifoname + "in"));

              }
            }
            printer.println(";;");
        }
      }
      printer.println("esac");



    }
}



public class Lc{
    static boolean DEBUG = false;
    static final int North = 0;
    static final int East = 1;
    static final int South = 2;
    static final int West = 3;
    static final String[] fifoprefix = {"North", "East", "South", "West"} ;
    static final int[] shelloutputs = { 1, 3, 5, 7} ;

    static final int[] shellinputs = {0, 4, 6, 8 } ;
    static final String fifodir = "fifos/";
    static Hashtable paths;
  //------------------------- SCC compiler - for the intel scc chip
    static void sccCompile(PAbsexpr[][] tiling, PrintStream printer, Hashtable types) {
      sccCompiler C = new sccCompiler(tiling);
      C.compile(printer);
    }

  //------------------------- Main shell script compiler
  //-----------static methods
    static String filenamefor(String[] inputFifoNames, String aname) {
      int d = todir(aname);
      if (d >= 0) {
        return inputFifoNames[d];
      }
      return aname;
    }

    static void compiletoshell(PAbsexpr[][] tiling, PrintStream printer, Hashtable types) {
      int y = tiling.length; int x = tiling[0].length;
    // for each position in the tiling we
    // generate 4 output fifo names
      String[][][] outputFifoNames = new String[y][x][4];
      String[][][] defaultoutputFifoNames = new String[y][x][4];
    // the input fifo names will be generated after
    // taking into account the presence
    // of mirror null and identity cells
      String[][][] inputFifoNames = new String[y][x][4];

      printer.println("rm fifos/*");
    // first we generate a default pattern of output fifo names
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            for (int dir = North; dir <= West; dir++) {
              String fifoname = fifodir + fifoprefix[dir] + row + "_" + col;
              printer.println("mkfifo " + fifoname);
              outputFifoNames[row][col][dir] = defaultoutputFifoNames[row][col][dir] = fifoname;
            }
        }
      }

    // now apply rotations and flips to the fifo output positions
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            resolveAtomicRotateAndFLip(tiling[row][col], outputFifoNames[row][col], 1);

        }

      }
    // now route the input channels
      for (int row = 0; row < y; row++) {
        for (int col = 0; col < x; col++) {
            if (! routingcell(tiling[row][col])) {
              for (int dir = 0; dir < 4; dir++) {
                inputFifoNames[row][col][dir] = trace(dir, row, col, outputFifoNames, tiling, tiling[row][col] instanceof AMirrorAbsexpr);
              }
              PAbsexpr thiscell = resolveAtomicRotateAndFLip(tiling[row][col], inputFifoNames[row][col], - 1);
              if (thiscell instanceof ABlockAbsexpr) {
                LinkedList<PAbsioassign> list = ((ABlockAbsexpr) thiscell) .getAbsioassign();
                Object[] assigns = list.toArray();
                printer.println("# starting block ");
                for (int i = 0; i < assigns.length; i++) {
          // handle io redirection for blocks
                    String command = "";
                    AAbsioassign assign = (AAbsioassign) assigns[i];
                    command = (assign).getIdsym().toString();
                    Process thisTask = new Process(command);
                    printer.println("# command " + i + " = " + command);
                    for (PAbsparams p : assign.getAbsparams()) {
                      if (p instanceof ARedirectionAbsparams) {
                        ARedirectionAbsparams rd = (ARedirectionAbsparams) p;
                        String op = rd.getInoutsym().getText();
                        String[] fn = op.equals("<") ? inputFifoNames[row][col] : defaultoutputFifoNames[row][col];
                        thisTask.add(new Rdir( filenamefor(fn, rd.getAbsstream().toString().trim()), rd.getInoutsym().getText()));
                      } else {
                        AStreamAbsparams s = (AStreamAbsparams) p;
                        thisTask.add(new Rdir( filenamefor(inputFifoNames[row][col], s.toString()), ""));
                      }
                    }
                    ;

                    printer.println(thisTask.toString() + " &");
                }
              } else
              {
				  printer.println("# node = " + tiling[row][col]);
	                if (thiscell instanceof AMirrorAbsexpr) {
	                    for (int dir = 0; dir < 4; dir++) {
	                      Process thisTask = new Process("cat ");
	                      thisTask.add(new Rdir(inputFifoNames[row][col][dir], " <"));
	                      thisTask.add(new Rdir(outputFifoNames[row][col][dir], " >"));
	                      if (! (inputFifoNames[row][col][dir].equals("/dev/null"))) {
	                        printer.println(thisTask.toString() + " &");
	                      }
	                    }
	                } else {
	                    String command = getShellPrimitive(tiling[row][col]) + " ";
	                    Process thisTask = new Process(command,(AAbsfaces)types.get(tiling[row][col].toString().trim()));
	                    for (int dir = 0; dir < 4; dir++) {
	                      thisTask.add(new Rdir(inputFifoNames[row][col][dir], " " + shellinputs[dir] + "<"));
	                      thisTask.add(new Rdir(outputFifoNames[row][col][dir], " " + shelloutputs[dir] + ">"));
	                    }
	                    printer.println(thisTask.toString() + " " + row + " " + col + "&");
	                }
              }

            }
        }
      }
     // printer.println("echo lino tasks launched");
    }
    static int todir(String direction) {
      direction = direction.trim();
      if (DEBUG) {
        System.out.println("todir (" + direction + ")");
      }

      for (int i = 0; i < 4; i++) {
        if (direction.equals(fifoprefix[i])) {
            return i;
        }

      }
      return - 1;
    }
    static String getShellPrimitive(PAbsexpr node) {
      if (node instanceof AFlipAbsexpr) {
        return getShellPrimitive(((AFlipAbsexpr) node).getAbsexpr());
      }

      if (node instanceof ARotateAbsexpr) {
        return getShellPrimitive(((ARotateAbsexpr) node).getAbsexpr());
      }

      if (paths.get(node.toString().trim()) != null) {
        return paths.get(node.toString().trim()).toString();
      }

      return node.toString();
    }
  // returns true if the cell only does routing
    static boolean routingcell(PAbsexpr node) { return node instanceof AIdentityAbsexpr || node instanceof ANullAbsexpr ; }
    static final int[] dx = {0, 1, 0, - 1} ;
    static final int[] dy = { - 1, 0, 1, 0} ;
    static final int[] reflect = {South, West, North, East} ;
    static String trace(int dir, int row, int col, String[][][] names, PAbsexpr[][] tiling, boolean sourceismirror) {

      if (DEBUG) {
        System.out.print("going " + fifoprefix[dir] + " from " + row + "," + col);
      }

    // compute next position
      row = row + dy[dir];
      col = col + dx[dir];
      if (DEBUG) {
        System.out.print(" arrive at" + row + "," + col);
      }

      if (row >= 0 && row < tiling.length && col >= 0 && col < tiling[0].length) {
        if (tiling[row][col] instanceof AMirrorAbsexpr) {      // go back the other way
            if (sourceismirror) {
              return "/dev/null" ;
            }
            return trace(reflect[dir], row, col, names, tiling, sourceismirror);
        }
    // continue this way
        if (tiling[row][col] instanceof AIdentityAbsexpr) {
            return trace(dir, row, col, names, tiling, sourceismirror);
        }

    // nothing comes from here
        if (tiling[row][col] instanceof ANullAbsexpr) {
            if (DEBUG) { System.out.print("\r"); }
            return "/dev/null";
        }
        if (DEBUG) {
            System.out.println(" select " + names[row][col][reflect[dir]]);
        }

    // otherwise return the output of the cell you are on
        return names[row][col][reflect[dir]];
      }
      if (DEBUG) {
        System.out.print("\r");
      }

      return "/dev/null";
    }

    static PAbsexpr resolveAtomicRotateAndFLip(PAbsexpr node, String[] names, int direction) {

      if (node instanceof AFlipAbsexpr) {    // exchange East and West names
        PAbsexpr flipnode = resolveAtomicRotateAndFLip(((AFlipAbsexpr) node).getAbsexpr(), names, direction);
        String temp = names[East];
        names[East] = names[West];
        names[West] = temp;
        return flipnode;
      } else {
        if (node instanceof ARotateAbsexpr) {      // exchange East and West names
            PAbsexpr newnode = resolveAtomicRotateAndFLip(((ARotateAbsexpr) node).getAbsexpr(), names, direction);
            String[] temp = new String[4];
            for (int i = 0; i < 4; i++) {
              temp[i] = names[((4 - direction) + i) % 4];
            }
            if (DEBUG) {
              System.out.println("in " + node + " replace");
            }
            for (int i = 0; i < 4; i++) {
              if (DEBUG) {
                System.out.println("\t" + fifoprefix[i] + " <- " + temp[i] + ",");
              }
              names[i] = temp[i];
            }
            return newnode;
        }
      }
      return node;
    }
  // ------------------ this handles comand line, parses, does rewrite rules and calls shell compiler
    public static void main(String[] arguments) {
      if (arguments.length < 2) {
            System.out.println("too few arguments" + arguments.length);
            System.out.println("usage:");
            System.out.println("lc [flags] sourcefile destfile  ");
            System.exit(1);
      }
      Parser parser;
      boolean printSyntax = false;
      boolean printTypes = false;
      boolean printMacros = true;
      boolean printPostMacrosubs = true;
      boolean printPostRep = true;
      boolean isscc = false;
      try {
        int start = 0;
        while (arguments[start].startsWith("-")) {
            if (arguments[start].equals("-scc")) { isscc = true; }
            start++;
        }
        if (arguments.length != 2 + start) {
			  System.out.println("usage:");
			  System.out.println("lc [flags] sourcefile destfile  ");
			  System.exit(1);
        }
        FileReader r = new FileReader(arguments[0 + start]);
        PrintStream shellfile = new PrintStream(new FileOutputStream(arguments[1 + start]));
        PushbackReader pr = new PushbackReader(new BufferedReader(r), 1024) ;
        Lexer lexer = new Lexer(pr);

        parser = new Parser(lexer);
        Node ast = parser.parse();
        if (printSyntax) {  System.out.println(ast); }

        Declfinder ids = new Declfinder();
        ast.apply(ids);
        Hashtable types = ids.types; paths = ids.paths;
        if (printTypes) { System.out.println(types);   }

        Macrofinder mf = new Macrofinder();
        ast.apply(mf);
        Hashtable macros = mf.macros;
        if (printMacros) { if(DEBUG)System.out.println(macros); }

        Macrosubs sub = new Macrosubs(macros);
        for (int reps = sub.repcount - 1; reps < sub.repcount;) { reps = sub.repcount; ast.apply(sub); }
		if (printPostMacrosubs) {
			if(DEBUG)System.out.println(macros );
		}

        HReplicator rep = new Flipper();
        for (int reps = rep.repcount - 1; reps < rep.repcount;) {
	        reps = rep.repcount; ast.apply(rep);
	        mf = new Macrofinder();
	        ast.apply(mf);
	        macros = mf.macros;
			if (printPostRep) if(DEBUG)System.out.println(macros + "\n");
        }
        ToArray ta = new ToArray();
        ast.apply(ta);
        Object mainone = macros.get("main");
        if (mainone != null) {
            PAbsexpr[][] picture = ta.pics.get(mainone);
            int[] widths = new int[picture[0].length];
            for (int col = 0; col < widths.length; col++) {
              widths[col] = 1;
              for (int row = 0; row < picture.length; row++) {
                if (widths[col] < picture[row][col].toString().length()) {
                    widths[col] = picture[row][col].toString().length();
                }
              }
            }
            if(DEBUG)System.out.println("final layout "+picture.length+"rows "+picture[0].length+"cols");
            for (int i = 0; i < picture.length; i++) {
	            for (int j = 0; j < picture[0].length; j++) {
		            String s = picture[i][j].toString();
		            if(DEBUG)System.out.print(s);
		            for (int k = 0; k < widths[j] - s.length(); k++) {
			             if(DEBUG)System.out.print(" ");
		            }
	            }
                 if(DEBUG)System.out.println("");
            }
			if (isscc) { sccCompile(picture, shellfile,types); } else {
			compiletoshell(picture, shellfile,types); }
        }
      } catch (Exception ex) { System.err.println(ex.toString()); }
    }
}// of Lc
///------------------------------------------------------ classes used in the compiler
class Process{  // represents a shell process that will run
    String pathname;
    List<Rdir> params = new LinkedList<Rdir>();
    boolean [] validinputs ={true, true, true, true};
    boolean [] validoutputs = { true, true, true, true};
    class channelvalidator extends DepthFirstAdapter{
	// this sets the valid inputs and outputs array given a list of abs faces
		int current =0;
		public void outAAbsface(AAbsface node)
		{
		 validinputs[current] = node.getIn().toString().trim()!="";
		 validoutputs[current] = node.getOut().toString().trim()!="";
		 current ++;
		}
	}
    Process(String path,lino.node.AAbsfaces types){ pathname = path; types.apply(new channelvalidator());}
    Process(String path ){ pathname = path;  }
    void add(Rdir param) {
		// this only adds parameters if the redirect is valid
		// for this type, id, if the programme wants to do
		// input or output on this channel
		String rd = param.rdirop;
		if(rd.contains(">")){
			int chan = channelfromRedir(rd);
			for(int i=0;i<4;i++)
				if(Lc.shelloutputs[i]==chan&& validoutputs[i])
					params.add(param);
		}
		if(rd.contains("<")){
			int chan = channelfromRedir(rd);
			for(int i=0;i<4;i++)
				if(Lc.shellinputs[i]==chan&& validinputs[i])
					params.add(param);
		}
	}
	int channelfromRedir(String s){
		if(s.equals("<"))return 0;
		if(s.equals(">")) return 1;
		return s.charAt(1) -'0';}
    void sortparams() { Collections.sort(params, new RdirComparator()); }
    public String toString() { String s = pathname + " ";
      sortparams();
      for (Rdir t : params) {
        s = s + t;
      }
      return s;
    }
}
class RdirComparator implements Comparator <Rdir> {
    public int compare(Rdir o1, Rdir o2) {
    //if(DEBUG)System.out.println("compare "+o1.sig() + ","+o2.sig()+"="+ (o1.sig().compareTo(o2.sig())));
      return o1.sig().compareTo(o2.sig());
    }
}
class Rdir{  // represents a file or fifo redirection
    String fname;
    String rdirop;
    Rdir(String name, String op){ fname = name; rdirop = op; }
    public String toString() { return rdirop + " " + fname + " "; }
    public String sig() { return fname + rdirop; }
}

class HReplicator extends DepthFirstAdapter {
    int repcount = 0;
    PAbsexpr Hlist(PAbsexpr e, int n) {
      if (n <= 1) { return (PAbsexpr) e.clone(); }
      return new AHorizAbsexpr((PAbsexpr) e.clone(), new TBesidesym(), Hlist(e, n - 1));
    }

    public void outARowAbsexpr(ARowAbsexpr node) {
      String sn = node.getNumbersym().getText();
      Integer N = new Integer(sn);
      PAbsexpr exp = node.getAbsexpr();
      int n = N;
      node.replaceBy(Hlist(exp, n)); repcount++;
    }
}
class VReplicator extends HReplicator {
    PAbsexpr Vlist(PAbsexpr e, int n) {
      if (n <= 1) { return (PAbsexpr) e.clone(); }
      return new AVertAbsexpr((PAbsexpr) e.clone(), new TAbovesym(), Vlist(e, n - 1));
    }
    public void outAColAbsexpr(AColAbsexpr node) {
      String sn = node.getNumbersym().getText();
      Integer N = new Integer(sn);
      PAbsexpr exp = node.getAbsexpr();
      int n = N;
      node.replaceBy(Vlist(exp, n)); repcount++;
    }

}
class ToArray extends DepthFirstAdapter {
    Hashtable<PAbsexpr, PAbsexpr[][]> pics = new Hashtable<PAbsexpr, PAbsexpr[][]>();
    public void defaultOut(Node node) {
      if (node instanceof PAbsexpr) {
        PAbsexpr[][] n = new PAbsexpr[1][1];
        n[0][0] = (PAbsexpr) node;
        pics.put(n[0][0], n);
      }
    }
    public void outAVertAbsexpr(AVertAbsexpr node) {
      PAbsexpr t = node.getTop();
      PAbsexpr b = node.getBottom();
      if (t != null & b != null) {
        PAbsexpr[][] T = pics.get(t); PAbsexpr[][] B = pics.get(b);
        int w = T[0].length;
        if (B[0].length != w) {
            System.out.println("width mismatch " + node);
            System.exit(- 1);
        }
        int h = T.length + B.length;
        PAbsexpr[][] n = new PAbsexpr[h][w];
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < T.length; j++) {
              n[j][i] = T[j][i];
            }
            for (int j = 0; j < B.length; j++) {
              n[ j + T.length][i] = B[j][i];
            }
        }
        pics.put(node, n);
      } else {
        if (Lc.DEBUG) {
            System.out.println("top and bottom arrays not both present for " + node);
        }
      }
    }
    public void outAHorizAbsexpr(AHorizAbsexpr node) {
      PAbsexpr l = node.getLeft();
      PAbsexpr r = node.getRight();
      if (l != null & r != null) {
        PAbsexpr[][] L = pics.get(l); PAbsexpr[][] R = pics.get(r);
        int h = L.length;
        if (R.length != h) {
            if (Lc.DEBUG) {
              System.out.println("Height mismatch " + node);
            }
        }

        int w = L[0].length + R[0].length;

        PAbsexpr[][] n = new PAbsexpr[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < L[0].length; j++) {
              n[i][j] = L[i][j];
            }
            for (int j = 0; j < R[0].length; j++) {
              n[i][ j + L[0].length] = R[i][j];
            }
        }
        pics.put(node, n);
      }
    }

}
class Flipper extends VReplicator {  // handles flips and rotates
    public void outARotateAbsexpr(ARotateAbsexpr node) {
      PAbsexpr e = node.getAbsexpr();
    //7. rot (e | f) =>(rot e)_(rot f)
      if (e instanceof AHorizAbsexpr) { AHorizAbsexpr E = (AHorizAbsexpr) e.clone();
        ARotateAbsexpr nl = new ARotateAbsexpr(new TRotatesym(), E.getLeft());
        PAbsexpr g = new AVertAbsexpr(nl, new TAbovesym(), new ARotateAbsexpr(new TRotatesym(), E.getRight()));
        if (Lc.DEBUG) {
            System.out.println("Replace " + node + " By " + g + " nl =" + nl);
        }

        node.replaceBy(g);
        repcount++;
      } else {
        if (e instanceof AVertAbsexpr) { AVertAbsexpr E = (AVertAbsexpr) e.clone();

            PAbsexpr g = new AHorizAbsexpr(new ARotateAbsexpr(new TRotatesym(), E.getBottom()), new TBesidesym(), new ARotateAbsexpr(new TRotatesym(), E.getTop()));
            if (Lc.DEBUG) {
              System.out.println("Replace " + node + " By " + g);
            }

            node.replaceBy(g);
            repcount++;
        }
      }
      if (e instanceof AMirrorAbsexpr || e instanceof AIdentityAbsexpr || e instanceof ANullAbsexpr) {
        node.replaceBy(e); repcount++;
      }
    }
    public void outAFlipAbsexpr(AFlipAbsexpr node) {
      TFlipsym f = new TFlipsym();
      PAbsexpr e = node.getAbsexpr();
      if (e instanceof AHorizAbsexpr) { AHorizAbsexpr E = (AHorizAbsexpr) e.clone();
        AFlipAbsexpr nl = new AFlipAbsexpr(new TFlipsym(), E.getRight());
        PAbsexpr g = new AHorizAbsexpr(nl, new TBesidesym(), new AFlipAbsexpr(f, E.getLeft()));
        node.replaceBy(g);
        repcount++;
      } else {
        if (e instanceof AVertAbsexpr) { AVertAbsexpr E = (AVertAbsexpr) e.clone();
            PAbsexpr g = new AVertAbsexpr(new AFlipAbsexpr(new TFlipsym(), E.getTop()), new TAbovesym(), new AFlipAbsexpr(f, E.getBottom()));
            node.replaceBy(g);
            repcount++;
        } else {
            if (e instanceof AMirrorAbsexpr || e instanceof AIdentityAbsexpr || e instanceof ANullAbsexpr) {
              node.replaceBy(e); repcount++;
            }
        }
      }
      if (e instanceof AFlipAbsexpr) { node.replaceBy(((AFlipAbsexpr) e).getAbsexpr()); repcount++; }
    }

}


class Macrosubs extends DepthFirstAdapter {
        Macrosubs(Hashtable t){ tab = t; }
        Hashtable tab;
        int repcount = 0;
        public void outAIdAbsexpr(AIdAbsexpr node) {
                String n = node.getIdsym().getText();
                Object rep = tab.get(n);
                if (rep != null) {
            //if(DEBUG)System.out.println("Replace "+node+" By "+rep);
                        node.replaceBy((Node) ((Node) rep).clone());
                        repcount++;
                }
        }


}
class Macrofinder extends  DepthFirstAdapter {
    Hashtable macros = new Hashtable();
    public void outAMacroAbsassign(AMacroAbsassign node) {
      macros.put(node.getIdsym().getText().toString().trim() , node.getAbsexpr()  );
    }
}// of maxrofinder
class Declfinder extends DepthFirstAdapter {
    Hashtable types = new Hashtable();
    Hashtable paths = new Hashtable();
    public void outADeclAbsassign(ADeclAbsassign node) {
      types.put(node.getLabel().getText(), node.getAbsfaces());
      paths.put(node.getLabel().getText(), node.getPath().toString().replace('"', ' ').trim());
    }
}// of Declfinder

